package com.oocl.euc.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.oocl.csc.frm.sso.exceptions.SSOException;
import com.oocl.euc.authentication.core.RoleInfo;
import com.oocl.euc.authentication.core.SSOAuthority;
import com.oocl.euc.authentication.core.SSOUserContext;
import com.oocl.euc.authentication.core.UserInfo;
import com.oocl.euc.authentication.tool.CookieHelper;
import com.oocl.euc.authentication.tool.SsoTokenHelper;
import com.oocl.euc.authentication.tool.WebHelper;
import com.oocl.euc.common.constant.ApplicationConstant;
import com.oocl.euc.core.maintenance.model.Role;
import com.oocl.euc.core.maintenance.model.Users;
import com.oocl.euc.core.maintenance.service.RoleService;
import com.oocl.euc.core.maintenance.service.UsersService;
import com.oocl.euc.framework.authentication.common.JwtToken;
import com.oocl.euc.framework.common.utils.CollectionUtils;
import com.oocl.euc.framework.response.Result;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class GenerateTokenFilter extends GenericFilterBean{
    private static Logger logger= LoggerFactory.getLogger(GenerateTokenFilter.class);
    private SsoTokenHelper tokenHelper;
    private CookieHelper cookieHelper;
    private WebHelper webHelper;
    private UsersService usersService;
    private RoleService roleService;

    public GenerateTokenFilter() {
    }

    public GenerateTokenFilter(SsoTokenHelper tokenHelper, CookieHelper cookieHelper,
                               UsersService usersService, RoleService roleService, WebHelper webHelper) {
        super();
        this.tokenHelper = tokenHelper;
        this.cookieHelper = cookieHelper;
        this.usersService = usersService;
        this.roleService = roleService;
        this.webHelper = webHelper;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest)request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        if(webHelper.isRest(httpRequest) || webHelper.isSkip(httpRequest)){
            chain.doFilter(request, response);
        } else {
            try {
                String token = (String) httpRequest.getAttribute(ApplicationConstant.OPENTOKEN_COOKIE);
                String domainId = (String) httpRequest.getAttribute(ApplicationConstant.USERID_COOKIE);
                if(StringUtils.isNotEmpty(token)){
                    if(StringUtils.isEmpty(domainId)) {
                        domainId = cookieHelper.getCookie(httpRequest, ApplicationConstant.USERID_COOKIE).getValue();
                    }
                    SSOUserContext userContext = getUserContext(domainId);
                    JwtToken accessToken = tokenHelper.createAccessTokenWithOpenToken(userContext, token);
                    Cookie jwtTokenCookie = cookieHelper.getCookie(httpRequest, ApplicationConstant.JWT_ACCESS_TOKEN);
                    cookieHelper.addOrRefreshTokenCookieForOOCL(httpServletResponse, jwtTokenCookie, ApplicationConstant.JWT_ACCESS_TOKEN, accessToken);
                    logger.info("Create or refresh JWT token!");
                    logger.info("SSO authentication successfully for user " + domainId);
                }
                chain.doFilter(request, response);
            } catch (SSOException | IllegalArgumentException e) {
                logger.error(GenerateTokenFilter.class.getSimpleName() + " SSO authentication failed...", e);
                httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
                new ObjectMapper().writeValue(httpServletResponse.getWriter(), new Result("USER NOT FOUND", e.getMessage(), null));
            }
        }
    }

    protected SSOUserContext getUserContext(String domainId) throws SSOException {
        Users users = getUsersService().findByDomainId(domainId);
        if(null == users) {
            throw new SSOException("Authentication Failed: User not found");
        }
        List<Role> roles = getRoleService().findRoleByStaffId(users.getStaffId());
        SSOUserContext userContext = new SSOUserContext();
        UserInfo userInfo = new UserInfo();
        userInfo.setId(users.getStaffId());
        userInfo.setDomainId(users.getDomainId());
        userInfo.setMail(users.getEmail());
        userInfo.setDisplayName(users.getDisplayName());
        userInfo.setPhone(users.getPhone());
        userContext.setUserInfo(userInfo);
        List<SSOAuthority> authorities = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(roles)) {
            roles.stream().map(role -> {
                RoleInfo roleInfo = new RoleInfo();
                roleInfo.setRoleId(role.getId());
                roleInfo.setAuthority(role.getRoleName());
                return authorities.add(roleInfo);
            }).collect(Collectors.toList());
        }
        userContext.setRoles(authorities);

        return userContext;
    }

    protected UsersService getUsersService() {
        return usersService;
    }

    protected RoleService getRoleService() {
        return roleService;
    }
}
